home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 February / EnigmA AMIGA RUN 04 (1996)(G.R. Edizioni)(IT)[!][issue 1996-02][Skylink CD III].iso / earcd / comm2 / termsorc.lha / Extras / Source / term-source.lha / termBuffer.c < prev    next >
C/C++ Source or Header  |  1995-09-26  |  29KB  |  1,419 lines

  1. /*
  2. **    termBuffer.c
  3. **
  4. **    Auxilary routines for text buffer/capture management.
  5. **
  6. **    Copyright © 1990-1995 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. */
  9.  
  10. #include "termGlobal.h"
  11.  
  12.     /* Search string window gadgets. */
  13.  
  14. enum    {    GAD_STRING=1000,GAD_OK,GAD_CANCEL };
  15.  
  16.     /* Maximum size of an allocated line string. */
  17.  
  18. #define STRING_SIZE    (1 + 255 + 1)
  19.  
  20.     /* How many strings to include in a single puddle. */
  21.  
  22. #define STRING_COUNT    10
  23.  
  24.     /* The number of lines the buffer will grow. */
  25.  
  26. #define BUFFER_GROW    100
  27.  
  28.     /* Memory pool header. */
  29.  
  30. STATIC APTR        BufferPoolHeader;
  31.  
  32.     /* Word separator characters. */
  33.  
  34. STATIC BYTE WordSeparators[256] =
  35. {
  36.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  37.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  38.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  39.     0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
  40.     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  41.     0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,
  42.     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  43.     0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,
  44.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  45.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  46.     0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  47.     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  48.     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  49.     0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
  50.     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  51.     0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0
  52. };
  53.  
  54.     /* AllocString(STRPTR String,WORD Len):
  55.      *
  56.      +    Allocate space for a string, new pooled version.
  57.      */
  58.  
  59. STATIC STRPTR __regargs
  60. AllocString(STRPTR String,LONG Len)
  61. {
  62.     STRPTR Mem;
  63.  
  64.     if(Len > 255)
  65.         Len = 255;
  66.  
  67.     if(Mem = (STRPTR)LibAllocPooled(BufferPoolHeader,1 + Len + 1))
  68.     {
  69.         *Mem++ = Len;
  70.  
  71.         if(Len)
  72.             CopyMem(String,Mem,Len);
  73.  
  74.         Mem[Len] = 0;
  75.  
  76.         return((STRPTR)Mem);
  77.     }
  78.     else
  79.         return(NULL);
  80. }
  81.  
  82.     /* FreeString(STRPTR String):
  83.      *
  84.      *    Free the space occupied by a string, new pooled version.
  85.      */
  86.  
  87. STATIC VOID __regargs
  88. FreeString(STRPTR String)
  89. {
  90.     LibFreePooled(BufferPoolHeader,&String[-1],1 + String[-1] + 1);
  91. }
  92.  
  93.     /* AddLine(STRPTR Line,LONG Size):
  94.      *
  95.      *    Add a line to the display buffer.
  96.      */
  97.  
  98. VOID __regargs
  99. AddLine(register STRPTR Line,register LONG Size)
  100. {
  101. /*
  102.     while(Size > 0 && Line[Size - 1] == ' ')
  103.         Size--;
  104. */
  105.         /* Are we still to update the buffer contents? */
  106.  
  107.     if(!BufferClosed)
  108.     {
  109.             /* Is the buffer array initialized? */
  110.  
  111.         if(BufferLines)
  112.         {
  113.             ULONG Signals = 0;
  114.  
  115.                 /* Pick up the global access semaphore
  116.                  * (two tasks are sharing the data).
  117.                  */
  118.  
  119.             ObtainSemaphore(BufferSemaphore);
  120.  
  121.                 /* Check for limit. */
  122.  
  123.             if(Config -> CaptureConfig -> MaxBufferSize && BufferSpace >= Config -> CaptureConfig -> MaxBufferSize)
  124.             {
  125.                 register LONG i;
  126.  
  127.                 BufferSpace -= BufferLines[0][-1];
  128.  
  129.                 FreeString(BufferLines[0]);
  130.  
  131.                 for(i = 1 ; i < MaxLines ; i++)
  132.                     BufferLines[i - 1] = BufferLines[i];
  133.  
  134.                 Lines--;
  135.  
  136.                     /* Tell the buffer task to
  137.                      * refresh the display.
  138.                      */
  139.  
  140.                 Signals = SIG_MOVEUP;
  141.             }
  142.             else
  143.             {
  144.                     /* We've reached the last line in the buffer. */
  145.  
  146.                 if(Lines == MaxLines)
  147.                 {
  148.                     STRPTR *MoreBuffer;
  149.  
  150.                         /* Allocate space for some more lines. */
  151.  
  152.                     if(MoreBuffer = (STRPTR *)AllocVecPooled((MaxLines + BUFFER_GROW) * sizeof(STRPTR),MEMF_ANY | MEMF_CLEAR))
  153.                     {
  154.                         register LONG i;
  155.  
  156.                         BufferChanged = TRUE;
  157.  
  158.                             /* Copy the old lines to the new
  159.                              * buffer.
  160.                              */
  161.  
  162.                         for(i = 0 ; i < Lines ; i++)
  163.                             MoreBuffer[i] = BufferLines[i];
  164.  
  165.                             /* Free the old lines. */
  166.  
  167.                         FreeVecPooled(BufferLines);
  168.  
  169.                             /* Set the new buffer. */
  170.  
  171.                         MaxLines += BUFFER_GROW;
  172.  
  173.                         BufferLines = MoreBuffer;
  174.                     }
  175.                     else
  176.                     {
  177.                         BufferChanged = TRUE;
  178.  
  179.                             /* We couldn't get enough memory
  180.                              * to extend the number of lines
  181.                              * in the buffer, so we'll have
  182.                              * to wrap the contents of the
  183.                              * buffer around.
  184.                              */
  185.  
  186.                         if(Lines)
  187.                         {
  188.                             register LONG i;
  189.  
  190.                             BufferSpace -= BufferLines[0][-1];
  191.  
  192.                             FreeString(BufferLines[0]);
  193.  
  194.                             for(i = 1 ; i < MaxLines ; i++)
  195.                                 BufferLines[i - 1] = BufferLines[i];
  196.  
  197.                             Lines--;
  198.  
  199.                                 /* Tell the buffer task to
  200.                                  * refresh the display.
  201.                                  */
  202.  
  203.                             Signals = SIG_MOVEUP;
  204.                         }
  205.                     }
  206.                 }
  207.             }
  208.  
  209.                 /* Allocate a new line and copy the buffer contents
  210.                  * into it.
  211.                  */
  212.  
  213.             if(BufferLines[Lines] = AllocString(Line,Size))
  214.             {
  215.                 BufferChanged = TRUE;
  216.  
  217.                 Lines++;
  218.  
  219.                 BufferSpace += Size;
  220.             }
  221.  
  222.             ReleaseSemaphore(BufferSemaphore);
  223.  
  224.                 /* Tell the buffer tasks to update the displays. */
  225.  
  226.             if(!Signals)
  227.                 Signals = SIG_UPDATE;
  228.  
  229.             NotifyBuffer(&BufferTaskSemaphore,&BufferInfoData,Signals);
  230.             NotifyBuffer(&ReviewTaskSemaphore,&ReviewInfoData,Signals);
  231.         }
  232.     }
  233. }
  234.  
  235.     /* DeleteBuffer():
  236.      *
  237.      *    Delete buffer resources.
  238.      */
  239.  
  240. VOID
  241. DeleteBuffer()
  242. {
  243.     if(BufferLines)
  244.     {
  245.         FreeVecPooled(BufferLines);
  246.  
  247.         BufferLines = NULL;
  248.     }
  249.  
  250.     if(BufferPoolHeader)
  251.     {
  252.         LibDeletePool(BufferPoolHeader);
  253.  
  254.         BufferPoolHeader = NULL;
  255.     }
  256.  
  257.     if(BufferSemaphore)
  258.     {
  259.         FreeVecPooled(BufferSemaphore);
  260.  
  261.         BufferSemaphore = NULL;
  262.     }
  263. }
  264.  
  265.     /* CreateBuffer():
  266.      *
  267.      *    Allocate buffer resources.
  268.      */
  269.  
  270. BYTE
  271. CreateBuffer()
  272. {
  273.     if(BufferLines = (STRPTR *)AllocVecPooled(MaxLines * sizeof(STRPTR),MEMF_ANY | MEMF_CLEAR | MEMF_PUBLIC))
  274.     {
  275.         if(BufferSemaphore = (struct SignalSemaphore *)AllocVecPooled(sizeof(struct SignalSemaphore),MEMF_ANY | MEMF_PUBLIC))
  276.         {
  277.             InitSemaphore(BufferSemaphore);
  278.  
  279.                 /* Create a memory pool header if possible. */
  280.  
  281.             if(BufferPoolHeader = LibCreatePool(MEMF_ANY | MEMF_PUBLIC,STRING_SIZE * STRING_COUNT,STRING_SIZE * STRING_COUNT))
  282.                 return(TRUE);
  283.         }
  284.  
  285.         FreeVecPooled(BufferLines);
  286.  
  287.         BufferLines = NULL;
  288.     }
  289.  
  290.     return(FALSE);
  291. }
  292.  
  293.     /* FreeBuffer():
  294.      *
  295.      *    Release the contents of the text buffer.
  296.      */
  297.  
  298. VOID
  299. FreeBuffer()
  300. {
  301.         /* Free the contents of the display buffer. */
  302.  
  303.     if(BufferLines)
  304.     {
  305.         APTR NewPoolHeader;
  306.  
  307.         ObtainSemaphore(BufferSemaphore);
  308.  
  309.             /* If a new pool header is available, free the old
  310.              * pool and replace it with the new pool.
  311.              */
  312.  
  313.         if(NewPoolHeader = LibCreatePool(MEMF_ANY | MEMF_PUBLIC,STRING_SIZE * STRING_COUNT,STRING_SIZE * STRING_COUNT))
  314.         {
  315.             LibDeletePool(BufferPoolHeader);
  316.  
  317.             BufferPoolHeader = NewPoolHeader;
  318.         }
  319.         else
  320.         {
  321.             LONG i;
  322.  
  323.             for(i = 0 ; i < Lines ; i++)
  324.             {
  325.                 if(BufferLines[i])
  326.                     FreeString(BufferLines[i]);
  327.             }
  328.         }
  329.  
  330.         FreeVecPooled(BufferLines);
  331.  
  332.         Lines = 0;
  333.  
  334.         MaxLines = BUFFER_GROW;
  335.  
  336.         BufferLines = (STRPTR *)AllocVecPooled(MaxLines * sizeof(STRPTR),MEMF_ANY | MEMF_CLEAR);
  337.  
  338.         ReleaseSemaphore(BufferSemaphore);
  339.  
  340.         NotifyBuffer(&BufferTaskSemaphore,&BufferInfoData,SIG_UPDATE);
  341.         NotifyBuffer(&ReviewTaskSemaphore,&ReviewInfoData,SIG_UPDATE);
  342.     }
  343.  
  344.     BufferSpace = 0;
  345.  
  346.     BufferChanged = FALSE;
  347. }
  348.  
  349.     /* DeleteSearchInfo(struct SearchInfo *Info):
  350.      *
  351.      *    Free buffer allocated by CreateSearchInfo().
  352.      */
  353.  
  354. VOID __regargs
  355. DeleteSearchInfo(struct SearchInfo *Info)
  356. {
  357.     if(Info)
  358.         FreeVecPooled(Info);
  359. }
  360.  
  361.     /* CreateSearchInfo(STRPTR Pattern):
  362.      *
  363.      *    Create auxilary data required by SearchTextBuffer().
  364.      */
  365.  
  366. struct SearchInfo * __regargs
  367. CreateSearchInfo(STRPTR Pattern,BOOLEAN Forward,BOOLEAN IgnoreCase,BOOLEAN WholeWords)
  368. {
  369.     struct SearchInfo *Info;
  370.  
  371.         /* Allocate the buffer. */
  372.  
  373.     if(Info = (struct SearchInfo *)AllocVecPooled(sizeof(struct SearchInfo),MEMF_ANY | MEMF_PUBLIC))
  374.     {
  375.         WORD i;
  376.  
  377.             /* Determine pattern width. */
  378.  
  379.         Info -> PatternWidth    = strlen(Pattern);
  380.         Info -> IgnoreCase    = IgnoreCase;
  381.  
  382.         if(Info -> PatternWidth == 1)
  383.         {
  384.             if(WordSeparators[Pattern[0]])
  385.                 WholeWords = FALSE;
  386.         }
  387.  
  388.         Info -> WholeWords = WholeWords;
  389.  
  390.             /* Turn the pattern into upper case characters. */
  391.  
  392.         if(IgnoreCase)
  393.         {
  394.             for(i = 0 ; i <= Info -> PatternWidth ; i++)
  395.                 Info -> Pattern[i] = ToUpper(Pattern[i]);
  396.         }
  397.         else
  398.         {
  399.             for(i = 0 ; i <= Info -> PatternWidth ; i++)
  400.                 Info -> Pattern[i] = Pattern[i];
  401.         }
  402.  
  403.             /* Fill the entire range with the maximum pattern width. */
  404.  
  405.         for(i = 0 ; i < 256 ; i++)
  406.             Info -> Distance[i] = Info -> PatternWidth;
  407.  
  408.             /* Fill in the matching distances. */
  409.  
  410.         if(Forward)
  411.         {
  412.             for(i = 0 ; i < Info -> PatternWidth - 1 ; i++)
  413.                 Info -> Distance[Info -> Pattern[i]] = Info -> PatternWidth - 1 - i;
  414.         }
  415.         else
  416.         {
  417.             for(i = Info -> PatternWidth - 1 ; i > 0 ; i--)
  418.                 Info -> Distance[Info -> Pattern[i]] = i;
  419.         }
  420.  
  421.             /* Restart from scratch. */
  422.  
  423.         Info -> FoundY    = -1;
  424.         Info -> Forward    = Forward;
  425.     }
  426.  
  427.     return(Info);
  428. }
  429.  
  430.     /* SearchTextBuffer():
  431.      *
  432.      *    String search function, based on the Boyer-Moore search
  433.      *    algorithm.
  434.      */
  435.  
  436. LONG __regargs
  437. SearchTextBuffer(struct SearchInfo *Info)
  438. {
  439.     if(BufferLines)
  440.     {
  441.         UBYTE    *Distance,
  442.             *Pattern;
  443.         WORD    LineWidth,
  444.             PatternWidth;
  445.         STRPTR    Line;
  446.  
  447.         LONG    i;
  448.         WORD    SearchPosition,PatternIndex,LineIndex,LastSearchPosition;
  449.  
  450.             /* Extract the relevant data. */
  451.  
  452.         Distance    = Info -> Distance;
  453.         Pattern        = Info -> Pattern;
  454.         PatternWidth    = Info -> PatternWidth;
  455.  
  456.         if(Info -> WholeWords)
  457.         {
  458.                 /* Which direction are we to search? */
  459.  
  460.             if(Info -> IgnoreCase)
  461.             {
  462.                 if(Info -> Forward)
  463.                 {
  464.                         /* Update the search positions. */
  465.  
  466.                     if(Info -> FoundY == -1)
  467.                     {
  468.                         Info -> FoundX        = 0;
  469.                         Info -> FoundY        = 0;
  470.                         LastSearchPosition    = 0;
  471.                     }
  472.                     else
  473.                     {
  474.                             /* Proceed to the next line. */
  475.  
  476.                         if(!(LastSearchPosition = Info -> Index))
  477.                             Info -> FoundY = (Info -> FoundY + 1) % Lines;
  478.                     }
  479.  
  480.                         /* Run down the buffer. */
  481.  
  482.                     for(i = Info -> FoundY ; i < Lines ; i++)
  483.                     {
  484.                         Line = BufferLines[i];
  485.  
  486.                             /* Is there anything to search for? */
  487.  
  488.                         if((LineWidth = Line[-1]) >= PatternWidth)
  489.                         {
  490.                                 /* Where are we to start searching? */
  491.  
  492.                             if(LastSearchPosition)
  493.                                 SearchPosition = LastSearchPosition;
  494.                             else
  495.                                 SearchPosition = PatternWidth;
  496.  
  497.                             do
  498.                             {
  499.                                     /* How many line characters
  500.                                      * match the pattern?
  501.                                      */
  502.  
  503.                                 PatternIndex    = PatternWidth - 1;
  504.                                 LineIndex    = SearchPosition - 1;
  505.  
  506.                                 while(PatternIndex >= 0 && Pattern[PatternIndex] == ToUpper(Line[LineIndex]))
  507.                                 {
  508.                                     LineIndex--;
  509.                                     PatternIndex--;
  510.                                 }
  511.  
  512.                                     /* Update the line search index
  513.                                      * for subsequent searches.
  514.                                      */
  515.  
  516.                                 SearchPosition += Distance[ToUpper(Line[SearchPosition - 1])];
  517.  
  518.                                     /* Found the pattern? */
  519.  
  520.                                 if(PatternIndex < 0)
  521.                                 {
  522.                                     LONG X = LineIndex + 1;
  523.  
  524.                                     if(X)
  525.                                     {
  526.                                         if(!WordSeparators[Line[X - 1]])
  527.                                             continue;
  528.                                         else
  529.                                         {
  530.                                             if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  531.                                                 continue;
  532.                                         }
  533.                                     }
  534.                                     else
  535.                                     {
  536.                                         if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  537.                                             continue;
  538.                                     }
  539.  
  540.                                         /* Store the position. */
  541.  
  542.                                     Info -> FoundX    = LineIndex + 1;
  543.                                     Info -> FoundY    = i;
  544.  
  545.                                         /* Remember column to start
  546.                                          * next search attempt at.
  547.                                          */
  548.  
  549.                                     if(SearchPosition <= LineWidth)
  550.                                         Info -> Index = SearchPosition;
  551.                                     else
  552.                                         Info -> Index = 0;
  553.  
  554.                                     return(i);
  555.                                 }
  556.                             }
  557.                             while(SearchPosition <= LineWidth);
  558.                         }
  559.  
  560.                             /* Reset search column. */
  561.  
  562.                         LastSearchPosition = 0;
  563.                     }
  564.                 }
  565.                 else
  566.                 {
  567.                         /* Update the search positions. */
  568.  
  569.                     if(Info -> FoundY == -1)
  570.                     {
  571.                         Info -> FoundX        = 0;
  572.                         Info -> FoundY        = Lines - 1;
  573.                         LastSearchPosition    = 0;
  574.                     }
  575.                     else
  576.                     {
  577.                         if((LastSearchPosition = Info -> Index) < 1)
  578.                         {
  579.                             if(Info -> FoundY)
  580.                                 Info -> FoundY--;
  581.                             else
  582.                                 Info -> FoundY = Lines - 1;
  583.                         }
  584.                     }
  585.  
  586.                         /* Run down the buffer. */
  587.  
  588.                     for(i = Info -> FoundY ; i >= 0 ; i--)
  589.                     {
  590.                         Line = BufferLines[i];
  591.  
  592.                             /* Is there anything to search for? */
  593.  
  594.                         if((LineWidth = Line[-1]) >= PatternWidth)
  595.                         {
  596.                                 /* Cast the magic spell of Boyer-Moore... */
  597.  
  598.                             if(LastSearchPosition < 1)
  599.                                 SearchPosition = LineWidth - (PatternWidth - 1);
  600.                             else
  601.                                 SearchPosition = LastSearchPosition;
  602.  
  603.                             do
  604.                             {
  605.                                 PatternIndex = 0;
  606.                                 LineIndex = SearchPosition - 1;
  607.  
  608.                                 while(PatternIndex < PatternWidth && Pattern[PatternIndex] == ToUpper(Line[LineIndex]))
  609.                                 {
  610.                                     LineIndex++;
  611.                                     PatternIndex++;
  612.                                 }
  613.  
  614.                                 SearchPosition -= Distance[ToUpper(Line[SearchPosition - 1])];
  615.  
  616.                                 if(PatternIndex == PatternWidth)
  617.                                 {
  618.                                     LONG X = LineIndex - PatternWidth;
  619.  
  620.                                     if(X)
  621.                                     {
  622.                                         if(!WordSeparators[Line[X - 1]])
  623.                                             continue;
  624.                                         else
  625.                                         {
  626.                                             if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  627.                                                 continue;
  628.                                         }
  629.                                     }
  630.                                     else
  631.                                     {
  632.                                         if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  633.                                             continue;
  634.                                     }
  635.  
  636.                                     Info -> FoundX    = LineIndex - PatternWidth;
  637.                                     Info -> FoundY    = i;
  638.                                     Info -> Index    = SearchPosition;
  639.  
  640.                                     return(i);
  641.                                 }
  642.                             }
  643.                             while(SearchPosition > 0);
  644.                         }
  645.  
  646.                         LastSearchPosition = 0;
  647.                     }
  648.                 }
  649.             }
  650.             else
  651.             {
  652.                 if(Info -> Forward)
  653.                 {
  654.                         /* Update the search positions. */
  655.  
  656.                     if(Info -> FoundY == -1)
  657.                     {
  658.                         Info -> FoundX        = 0;
  659.                         Info -> FoundY        = 0;
  660.                         LastSearchPosition    = 0;
  661.                     }
  662.                     else
  663.                     {
  664.                             /* Proceed to the next line. */
  665.  
  666.                         if(!(LastSearchPosition = Info -> Index))
  667.                             Info -> FoundY = (Info -> FoundY + 1) % Lines;
  668.                     }
  669.  
  670.                         /* Run down the buffer. */
  671.  
  672.                     for(i = Info -> FoundY ; i < Lines ; i++)
  673.                     {
  674.                         Line = BufferLines[i];
  675.  
  676.                             /* Is there anything to search for? */
  677.  
  678.                         if((LineWidth = Line[-1]) >= PatternWidth)
  679.                         {
  680.                                 /* Where are we to start searching? */
  681.  
  682.                             if(LastSearchPosition)
  683.                                 SearchPosition = LastSearchPosition;
  684.                             else
  685.                                 SearchPosition = PatternWidth;
  686.  
  687.                             do
  688.                             {
  689.                                     /* How many line characters
  690.                                      * match the pattern?
  691.                                      */
  692.  
  693.                                 PatternIndex    = PatternWidth - 1;
  694.                                 LineIndex    = SearchPosition - 1;
  695.  
  696.                                 while(PatternIndex >= 0 && Pattern[PatternIndex] == Line[LineIndex])
  697.                                 {
  698.                                     LineIndex--;
  699.                                     PatternIndex--;
  700.                                 }
  701.  
  702.                                     /* Update the line search index
  703.                                      * for subsequent searches.
  704.                                      */
  705.  
  706.                                 SearchPosition += Distance[Line[SearchPosition - 1]];
  707.  
  708.                                     /* Found the pattern? */
  709.  
  710.                                 if(PatternIndex < 0)
  711.                                 {
  712.                                     LONG X = LineIndex + 1;
  713.  
  714.                                     if(X)
  715.                                     {
  716.                                         if(!WordSeparators[Line[X - 1]])
  717.                                             continue;
  718.                                         else
  719.                                         {
  720.                                             if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  721.                                                 continue;
  722.                                         }
  723.                                     }
  724.                                     else
  725.                                     {
  726.                                         if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  727.                                             continue;
  728.                                     }
  729.  
  730.                                         /* Store the position. */
  731.  
  732.                                     Info -> FoundX    = LineIndex + 1;
  733.                                     Info -> FoundY    = i;
  734.  
  735.                                         /* Remember column to start
  736.                                          * next search attempt at.
  737.                                          */
  738.  
  739.                                     if(SearchPosition <= LineWidth)
  740.                                         Info -> Index = SearchPosition;
  741.                                     else
  742.                                         Info -> Index = 0;
  743.  
  744.                                     return(i);
  745.                                 }
  746.                             }
  747.                             while(SearchPosition <= LineWidth);
  748.                         }
  749.  
  750.                             /* Reset search column. */
  751.  
  752.                         LastSearchPosition = 0;
  753.                     }
  754.                 }
  755.                 else
  756.                 {
  757.                         /* Update the search positions. */
  758.  
  759.                     if(Info -> FoundY == -1)
  760.                     {
  761.                         Info -> FoundX        = 0;
  762.                         Info -> FoundY        = Lines - 1;
  763.                         LastSearchPosition    = 0;
  764.                     }
  765.                     else
  766.                     {
  767.                         if((LastSearchPosition = Info -> Index) < 1)
  768.                         {
  769.                             if(Info -> FoundY)
  770.                                 Info -> FoundY--;
  771.                             else
  772.                                 Info -> FoundY = Lines - 1;
  773.                         }
  774.                     }
  775.  
  776.                         /* Run down the buffer. */
  777.  
  778.                     for(i = Info -> FoundY ; i >= 0 ; i--)
  779.                     {
  780.                         Line = BufferLines[i];
  781.  
  782.                             /* Is there anything to search for? */
  783.  
  784.                         if((LineWidth = Line[-1]) >= PatternWidth)
  785.                         {
  786.                                 /* Cast the magic spell of Boyer-Moore... */
  787.  
  788.                             if(LastSearchPosition < 1)
  789.                                 SearchPosition = LineWidth - (PatternWidth - 1);
  790.                             else
  791.                                 SearchPosition = LastSearchPosition;
  792.  
  793.                             do
  794.                             {
  795.                                 PatternIndex = 0;
  796.                                 LineIndex = SearchPosition - 1;
  797.  
  798.                                 while(PatternIndex < PatternWidth && Pattern[PatternIndex] == Line[LineIndex])
  799.                                 {
  800.                                     LineIndex++;
  801.                                     PatternIndex++;
  802.                                 }
  803.  
  804.                                 SearchPosition -= Distance[Line[SearchPosition - 1]];
  805.  
  806.                                 if(PatternIndex == PatternWidth)
  807.                                 {
  808.                                     LONG X = LineIndex - PatternWidth;
  809.  
  810.                                     if(X)
  811.                                     {
  812.                                         if(!WordSeparators[Line[X - 1]])
  813.                                             continue;
  814.                                         else
  815.                                         {
  816.                                             if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  817.                                                 continue;
  818.                                         }
  819.                                     }
  820.                                     else
  821.                                     {
  822.                                         if(X + PatternWidth < LineWidth && !WordSeparators[Line[X + PatternWidth]])
  823.                                             continue;
  824.                                     }
  825.  
  826.                                     Info -> FoundX    = LineIndex - PatternWidth;
  827.                                     Info -> FoundY    = i;
  828.                                     Info -> Index    = SearchPosition;
  829.  
  830.                                     return(i);
  831.                                 }
  832.                             }
  833.                             while(SearchPosition > 0);
  834.                         }
  835.  
  836.                         LastSearchPosition = 0;
  837.                     }
  838.                 }
  839.             }
  840.         }
  841.         else
  842.         {
  843.                 /* Which direction are we to search? */
  844.  
  845.             if(Info -> IgnoreCase)
  846.             {
  847.                 if(Info -> Forward)
  848.                 {
  849.                         /* Update the search positions. */
  850.  
  851.                     if(Info -> FoundY == -1)
  852.                     {
  853.                         Info -> FoundX        = 0;
  854.                         Info -> FoundY        = 0;
  855.                         LastSearchPosition    = 0;
  856.                     }
  857.                     else
  858.                     {
  859.                             /* Proceed to the next line. */
  860.  
  861.                         if(!(LastSearchPosition = Info -> Index))
  862.                             Info -> FoundY = (Info -> FoundY + 1) % Lines;
  863.                     }
  864.  
  865.                         /* Run down the buffer. */
  866.  
  867.                     for(i = Info -> FoundY ; i < Lines ; i++)
  868.                     {
  869.                         Line = BufferLines[i];
  870.  
  871.                             /* Is there anything to search for? */
  872.  
  873.                         if((LineWidth = Line[-1]) >= PatternWidth)
  874.                         {
  875.                                 /* Where are we to start searching? */
  876.  
  877.                             if(LastSearchPosition)
  878.                                 SearchPosition = LastSearchPosition;
  879.                             else
  880.                                 SearchPosition = PatternWidth;
  881.  
  882.                             do
  883.                             {
  884.                                     /* How many line characters
  885.                                      * match the pattern?
  886.                                      */
  887.  
  888.                                 PatternIndex    = PatternWidth - 1;
  889.                                 LineIndex    = SearchPosition - 1;
  890.  
  891.                                 while(PatternIndex >= 0 && Pattern[PatternIndex] == ToUpper(Line[LineIndex]))
  892.                                 {
  893.                                     LineIndex--;
  894.                                     PatternIndex--;
  895.                                 }
  896.  
  897.                                     /* Update the line search index
  898.                                      * for subsequent searches.
  899.                                      */
  900.  
  901.                                 SearchPosition += Distance[ToUpper(Line[SearchPosition - 1])];
  902.  
  903.                                     /* Found the pattern? */
  904.  
  905.                                 if(PatternIndex < 0)
  906.                                 {
  907.                                         /* Store the position. */
  908.  
  909.                                     Info -> FoundX    = LineIndex + 1;
  910.                                     Info -> FoundY    = i;
  911.  
  912.                                         /* Remember column to start
  913.                                          * next search attempt at.
  914.                                          */
  915.  
  916.                                     if(SearchPosition <= LineWidth)
  917.                                         Info -> Index = SearchPosition;
  918.                                     else
  919.                                         Info -> Index = 0;
  920.  
  921.                                     return(i);
  922.                                 }
  923.                             }
  924.                             while(SearchPosition <= LineWidth);
  925.                         }
  926.  
  927.                             /* Reset search column. */
  928.  
  929.                         LastSearchPosition = 0;
  930.                     }
  931.                 }
  932.                 else
  933.                 {
  934.                         /* Update the search positions. */
  935.  
  936.                     if(Info -> FoundY == -1)
  937.                     {
  938.                         Info -> FoundX        = 0;
  939.                         Info -> FoundY        = Lines - 1;
  940.                         LastSearchPosition    = 0;
  941.                     }
  942.                     else
  943.                     {
  944.                         if((LastSearchPosition = Info -> Index) < 1)
  945.                         {
  946.                             if(Info -> FoundY)
  947.                                 Info -> FoundY--;
  948.                             else
  949.                                 Info -> FoundY = Lines - 1;
  950.                         }
  951.                     }
  952.  
  953.                         /* Run down the buffer. */
  954.  
  955.                     for(i = Info -> FoundY ; i >= 0 ; i--)
  956.                     {
  957.                         Line = BufferLines[i];
  958.  
  959.                             /* Is there anything to search for? */
  960.  
  961.                         if((LineWidth = Line[-1]) >= PatternWidth)
  962.                         {
  963.                                 /* Cast the magic spell of Boyer-Moore... */
  964.  
  965.                             if(LastSearchPosition < 1)
  966.                                 SearchPosition = LineWidth - (PatternWidth - 1);
  967.                             else
  968.                                 SearchPosition = LastSearchPosition;
  969.  
  970.                             do
  971.                             {
  972.                                 PatternIndex = 0;
  973.                                 LineIndex = SearchPosition - 1;
  974.  
  975.                                 while(PatternIndex < PatternWidth && Pattern[PatternIndex] == ToUpper(Line[LineIndex]))
  976.                                 {
  977.                                     LineIndex++;
  978.                                     PatternIndex++;
  979.                                 }
  980.  
  981.                                 SearchPosition -= Distance[ToUpper(Line[SearchPosition - 1])];
  982.  
  983.                                 if(PatternIndex == PatternWidth)
  984.                                 {
  985.                                     Info -> FoundX    = LineIndex - PatternWidth;
  986.                                     Info -> FoundY    = i;
  987.                                     Info -> Index    = SearchPosition;
  988.  
  989.                                     return(i);
  990.                                 }
  991.                             }
  992.                             while(SearchPosition > 0);
  993.                         }
  994.  
  995.                         LastSearchPosition = 0;
  996.                     }
  997.                 }
  998.             }
  999.             else
  1000.             {
  1001.                 if(Info -> Forward)
  1002.                 {
  1003.                         /* Update the search positions. */
  1004.  
  1005.                     if(Info -> FoundY == -1)
  1006.                     {
  1007.                         Info -> FoundX        = 0;
  1008.                         Info -> FoundY        = 0;
  1009.                         LastSearchPosition    = 0;
  1010.                     }
  1011.                     else
  1012.                     {
  1013.                             /* Proceed to the next line. */
  1014.  
  1015.                         if(!(LastSearchPosition = Info -> Index))
  1016.                             Info -> FoundY = (Info -> FoundY + 1) % Lines;
  1017.                     }
  1018.  
  1019.                         /* Run down the buffer. */
  1020.  
  1021.                     for(i = Info -> FoundY ; i < Lines ; i++)
  1022.                     {
  1023.                         Line = BufferLines[i];
  1024.  
  1025.                             /* Is there anything to search for? */
  1026.  
  1027.                         if((LineWidth = Line[-1]) >= PatternWidth)
  1028.                         {
  1029.                                 /* Where are we to start searching? */
  1030.  
  1031.                             if(LastSearchPosition)
  1032.                                 SearchPosition = LastSearchPosition;
  1033.                             else
  1034.                                 SearchPosition = PatternWidth;
  1035.  
  1036.                             do
  1037.                             {
  1038.                                     /* How many line characters
  1039.                                      * match the pattern?
  1040.                                      */
  1041.  
  1042.                                 PatternIndex    = PatternWidth - 1;
  1043.                                 LineIndex    = SearchPosition - 1;
  1044.  
  1045.                                 while(PatternIndex >= 0 && Pattern[PatternIndex] == Line[LineIndex])
  1046.                                 {
  1047.                                     LineIndex--;
  1048.                                     PatternIndex--;
  1049.                                 }
  1050.  
  1051.                                     /* Update the line search index
  1052.                                      * for subsequent searches.
  1053.                                      */
  1054.  
  1055.                                 SearchPosition += Distance[Line[SearchPosition - 1]];
  1056.  
  1057.                                     /* Found the pattern? */
  1058.  
  1059.                                 if(PatternIndex < 0)
  1060.                                 {
  1061.                                         /* Store the position. */
  1062.  
  1063.                                     Info -> FoundX    = LineIndex + 1;
  1064.                                     Info -> FoundY    = i;
  1065.  
  1066.                                         /* Remember column to start
  1067.                                          * next search attempt at.
  1068.                                          */
  1069.  
  1070.                                     if(SearchPosition <= LineWidth)
  1071.                                         Info -> Index = SearchPosition;
  1072.                                     else
  1073.                                         Info -> Index = 0;
  1074.  
  1075.                                     return(i);
  1076.                                 }
  1077.                             }
  1078.                             while(SearchPosition <= LineWidth);
  1079.                         }
  1080.  
  1081.                             /* Reset search column. */
  1082.  
  1083.                         LastSearchPosition = 0;
  1084.                     }
  1085.                 }
  1086.                 else
  1087.                 {
  1088.                         /* Update the search positions. */
  1089.  
  1090.                     if(Info -> FoundY == -1)
  1091.                     {
  1092.                         Info -> FoundX        = 0;
  1093.                         Info -> FoundY        = Lines - 1;
  1094.                         LastSearchPosition    = 0;
  1095.                     }
  1096.                     else
  1097.                     {
  1098.                         if((LastSearchPosition = Info -> Index) < 1)
  1099.                         {
  1100.                             if(Info -> FoundY)
  1101.                                 Info -> FoundY--;
  1102.                             else
  1103.                                 Info -> FoundY = Lines - 1;
  1104.                         }
  1105.                     }
  1106.  
  1107.                         /* Run down the buffer. */
  1108.  
  1109.                     for(i = Info -> FoundY ; i >= 0 ; i--)
  1110.                     {
  1111.                         Line = BufferLines[i];
  1112.  
  1113.                             /* Is there anything to search for? */
  1114.  
  1115.                         if((LineWidth = Line[-1]) >= PatternWidth)
  1116.                         {
  1117.                                 /* Cast the magic spell of Boyer-Moore... */
  1118.  
  1119.                             if(LastSearchPosition < 1)
  1120.                                 SearchPosition = LineWidth - (PatternWidth - 1);
  1121.                             else
  1122.                                 SearchPosition = LastSearchPosition;
  1123.  
  1124.                             do
  1125.                             {
  1126.                                 PatternIndex = 0;
  1127.                                 LineIndex = SearchPosition - 1;
  1128.  
  1129.                                 while(PatternIndex < PatternWidth && Pattern[PatternIndex] == Line[LineIndex])
  1130.                                 {
  1131.                                     LineIndex++;
  1132.                                     PatternIndex++;
  1133.                                 }
  1134.  
  1135.                                 SearchPosition -= Distance[Line[SearchPosition - 1]];
  1136.  
  1137.                                 if(PatternIndex == PatternWidth)
  1138.                                 {
  1139.                                     Info -> FoundX    = LineIndex - PatternWidth;
  1140.                                     Info -> FoundY    = i;
  1141.                                     Info -> Index    = SearchPosition;
  1142.  
  1143.                                     return(i);
  1144.                                 }
  1145.                             }
  1146.                             while(SearchPosition > 0);
  1147.                         }
  1148.  
  1149.                         LastSearchPosition = 0;
  1150.                     }
  1151.                 }
  1152.             }
  1153.         }
  1154.     }
  1155.  
  1156.     return(-1);
  1157. }
  1158.  
  1159. STATIC ULONG __asm __saveds
  1160. HistoryFunc(register __a0 struct Hook *Hook,register __a1 STRPTR NewString)
  1161. {
  1162.     struct List *List = (struct List *)Hook -> h_Data;
  1163.  
  1164.     if(NewString)
  1165.     {
  1166.         struct Node *Node = CreateNode(NewString);
  1167.  
  1168.         if(Node)
  1169.             AddTail(List,Node);
  1170.         else
  1171.             return(FALSE);
  1172.     }
  1173.     else
  1174.     {
  1175.         struct Node *Node = RemHead(List);
  1176.  
  1177.         FreeVecPooled(Node);
  1178.     }
  1179.  
  1180.     return(TRUE);
  1181. }
  1182.  
  1183. BOOLEAN __regargs
  1184. HandleSearchMessage(struct SearchContext *Context,struct IntuiMessage **MessagePtr)
  1185. {
  1186.     struct IntuiMessage    *Message;
  1187.     BOOLEAN             Done = FALSE;
  1188.  
  1189.     if(Message = GT_FilterIMsg(*MessagePtr))
  1190.     {
  1191.         ULONG         MsgClass    = Message -> Class,
  1192.                  MsgQualifier    = Message -> Qualifier;
  1193.         struct Gadget    *MsgGadget    = (struct Gadget *)Message -> IAddress;
  1194.         UWORD         MsgCode    = Message -> Code;
  1195.  
  1196.         LT_HandleInput(Context -> SearchHandle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);
  1197.  
  1198.         if(MsgClass == IDCMP_CLOSEWINDOW)
  1199.             Done = TRUE;
  1200.  
  1201.         if(MsgClass == IDCMP_GADGETUP)
  1202.         {
  1203.             switch(MsgGadget -> GadgetID)
  1204.             {
  1205.                 case GAD_STRING:
  1206.  
  1207.                     if(MsgCode == '\r')
  1208.                     {
  1209.                         if(Context -> LocalBuffer[0])
  1210.                         {
  1211.                             strcpy(Context -> Buffer,Context -> LocalBuffer);
  1212.  
  1213.                             Context -> Ok = TRUE;
  1214.  
  1215.                             LT_PressButton(Context -> SearchHandle,GAD_OK);
  1216.                         }
  1217.                         else
  1218.                             LT_PressButton(Context -> SearchHandle,GAD_CANCEL);
  1219.  
  1220.                         Done = TRUE;
  1221.                     }
  1222.  
  1223.                     break;
  1224.  
  1225.                 case GAD_OK:
  1226.  
  1227.                     LT_UpdateStrings(Context -> SearchHandle);
  1228.  
  1229.                     if(Context -> LocalBuffer[0])
  1230.                     {
  1231.                         strcpy(Context -> Buffer,Context -> LocalBuffer);
  1232.  
  1233.                         Context -> Ok = TRUE;
  1234.                     }
  1235.  
  1236.                     Done = TRUE;
  1237.  
  1238.                     break;
  1239.  
  1240.                 case GAD_CANCEL:
  1241.  
  1242.                     Done = TRUE;
  1243.  
  1244.                     break;
  1245.             }
  1246.         }
  1247.  
  1248.         GT_PostFilterIMsg(Message);
  1249.     }
  1250.  
  1251.     ReplyMsg(*MessagePtr);
  1252.  
  1253.     *MessagePtr = NULL;
  1254.  
  1255.     return(Done);
  1256. }
  1257.  
  1258. VOID __regargs
  1259. DeleteSearchContext(struct SearchContext *Context)
  1260. {
  1261.     if(Context)
  1262.     {
  1263.         LT_DeleteHandle(Context -> SearchHandle);
  1264.  
  1265.         DISPOSE(Context);
  1266.     }
  1267. }
  1268.  
  1269. struct SearchContext * __regargs
  1270. CreateSearchContext(struct Window *ParentWindow,STRPTR Buffer,struct Hook *HistoryHook,BOOLEAN *Forward,BOOLEAN *IgnoreCase,BOOLEAN *WholeWords)
  1271. {
  1272.     struct SearchContext *Context;
  1273.  
  1274.     if(NEW(Context))
  1275.     {
  1276.         struct LayoutHandle *Handle;
  1277.  
  1278.         Context -> Buffer = Buffer;
  1279.  
  1280.         if(HistoryHook)
  1281.             HistoryHook -> h_Entry = (HOOKFUNC)HistoryFunc;
  1282.  
  1283.         if(Handle = LT_CreateHandleTags(ParentWindow -> WScreen,
  1284.             LH_LocaleHook,    &LocaleHook,
  1285.         TAG_DONE))
  1286.         {
  1287.             struct Window *Window;
  1288.  
  1289.             LT_New(Handle,
  1290.                 LA_Type,    VERTICAL_KIND,
  1291.             TAG_DONE);
  1292.             {
  1293.                 LT_New(Handle,
  1294.                     LA_Type,    HORIZONTAL_KIND,
  1295. //                    LA_LabelID,    MSG_TERMBUFFER_ENTER_SEARCH_STRING_TXT,
  1296.                 TAG_DONE);
  1297.                 {
  1298.                     LT_New(Handle,
  1299.                         LA_Type,    VERTICAL_KIND,
  1300.                     TAG_DONE);
  1301.                     {
  1302.                         LT_New(Handle,
  1303.                             LA_Type,        STRING_KIND,
  1304.                             LA_LabelID,        MSG_V36_0788,
  1305.                             LA_STRPTR,        Context -> LocalBuffer,
  1306.                             LA_Chars,        30,
  1307.                             LA_ID,            GAD_STRING,
  1308.                             LAST_HistoryLines,    MAX(Config -> CaptureConfig -> SearchHistory,1),
  1309.                             LAST_HistoryHook,    HistoryHook,
  1310.                             GTST_MaxChars,        255,
  1311.                         TAG_DONE);
  1312.  
  1313.                         LT_EndGroup(Handle);
  1314.                     }
  1315.  
  1316.                     LT_New(Handle,
  1317.                         LA_Type,    VERTICAL_KIND,
  1318.                     TAG_DONE);
  1319.                     {
  1320.                         LT_New(Handle,
  1321.                             LA_Type,YBAR_KIND,
  1322.                         TAG_DONE);
  1323.  
  1324.                         LT_EndGroup(Handle);
  1325.                     }
  1326.  
  1327.                     LT_New(Handle,
  1328.                         LA_Type,    VERTICAL_KIND,
  1329.                     TAG_DONE);
  1330.                     {
  1331.                         LT_New(Handle,
  1332.                             LA_Type,    CHECKBOX_KIND,
  1333.                             LA_LabelID,    MSG_TERMBUFFER_SEARCH_FORWARD_TXT,
  1334.                             LA_BYTE,    Forward,
  1335.                         TAG_DONE);
  1336.  
  1337.                         LT_New(Handle,
  1338.                             LA_Type,    CHECKBOX_KIND,
  1339.                             LA_LabelID,    MSG_TEXTBUFFER_IGNORE_CASE_GAD,
  1340.                             LA_BYTE,    IgnoreCase,
  1341.                         TAG_DONE);
  1342.  
  1343.                         LT_New(Handle,
  1344.                             LA_Type,    CHECKBOX_KIND,
  1345.                             LA_LabelID,    MSG_SEARCH_ONLY_WHOLE_WORDS_TXT,
  1346.                             LA_BYTE,    WholeWords,
  1347.                         TAG_DONE);
  1348.  
  1349.                         LT_EndGroup(Handle);
  1350.                     }
  1351.  
  1352.                     LT_EndGroup(Handle);
  1353.                 }
  1354.  
  1355.                 LT_New(Handle,
  1356.                     LA_Type,    VERTICAL_KIND,
  1357.                 TAG_DONE);
  1358.                 {
  1359.                     LT_New(Handle,LA_Type,XBAR_KIND,LAXB_FullSize,TRUE,TAG_DONE);
  1360.  
  1361.                     LT_EndGroup(Handle);
  1362.                 }
  1363.  
  1364.                 LT_New(Handle,LA_Type,HORIZONTAL_KIND,
  1365.                     LAGR_SameSize,    TRUE,
  1366.                     LAGR_Spread,    TRUE,
  1367.                 TAG_DONE);
  1368.                 {
  1369.                     LT_New(Handle,
  1370.                         LA_Type,    BUTTON_KIND,
  1371.                         LA_LabelID,    MSG_TERMXPR_OKAY_GAD,
  1372.                         LA_ID,        GAD_OK,
  1373.                         LABT_ReturnKey,    TRUE,
  1374.                         LABT_ExtraFat,    TRUE,
  1375.                     TAG_DONE);
  1376.  
  1377.                     LT_New(Handle,
  1378.                         LA_Type,    BUTTON_KIND,
  1379.                         LA_LabelID,    MSG_GLOBAL_CANCEL_GAD,
  1380.                         LA_ID,        GAD_CANCEL,
  1381.                         LABT_EscKey,    TRUE,
  1382.                         LABT_ExtraFat,    TRUE,
  1383.                     TAG_DONE);
  1384.  
  1385.                     LT_EndGroup(Handle);
  1386.                 }
  1387.  
  1388.                 LT_EndGroup(Handle);
  1389.             }
  1390.  
  1391.             if(Window = LT_Build(Handle,
  1392.                 LAWN_TitleID,        MSG_TERMBUFFER_ENTER_SEARCH_STRING_TXT,
  1393.                 LAWN_IDCMP,        IDCMP_CLOSEWINDOW,
  1394.                 LAWN_HelpHook,        &GuideHook,
  1395.                 LAWN_Parent,        ParentWindow,
  1396.                 LAWN_UserPort,        ParentWindow -> UserPort,
  1397.                 WA_DepthGadget,        TRUE,
  1398.                 WA_CloseGadget,        TRUE,
  1399.                 WA_DragBar,        TRUE,
  1400.                 WA_RMBTrap,        TRUE,
  1401.                 WA_Activate,        TRUE,
  1402.                 WA_SimpleRefresh,    TRUE,
  1403.             TAG_DONE))
  1404.             {
  1405.                 LT_Activate(Handle,GAD_STRING);
  1406.  
  1407.                 Context -> SearchHandle    = Handle;
  1408.                 Context -> SearchWindow = Window;
  1409.  
  1410.                 return(Context);
  1411.             }
  1412.         }
  1413.  
  1414.         DISPOSE(Context);
  1415.     }
  1416.  
  1417.     return(NULL);
  1418. }
  1419.